package feri.lenobe;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

import feri.lenobe.PrevodRisanje.posameznZnak;

import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.PointF;
import android.widget.Toast;

//Razred za delo z Nevronsko Mrežo
public class NevronskaMreza extends Activity {
	private Nivo vhod;
	private Nivo skriti;
	private Nivo izhod;
	private Random rnd;
	public int stevilo = 0;
	
	//Nastavitve (DODAL ANDREJ)
	private float HitrostUcenja;
	private int StVhodnihN;
	private int StSkritihN;
	private float DovoljenaNapaka;
	
	//Konstruktor Nevronske Mreže
	public NevronskaMreza(float hitrostUcenja, int stSkritihN, float dovoljenaNapaka){
		HitrostUcenja = hitrostUcenja;
		StSkritihN = stSkritihN;
		DovoljenaNapaka = dovoljenaNapaka;
	}
	
	//Učenje nevronske mreže
	public void Nauci(List<posameznZnak> seznamznakov){
		stevilo = seznamznakov.size();
		StVhodnihN = seznamznakov.get(0).vektorjiz.size() * 2;
		InicilizirajMrezo();
		Treniraj(seznamznakov);
	}
	
	//Razpoznavanje znaka
	public int Razpoznaj(List<PointF> seznamtock){
		DodajVhode(seznamtock);
		AktivirajNivo(skriti, vhod);
		AktivirajNivo(izhod, skriti);
		return Najdi();
	}
	
	//Shranjevanje uteži v mreži v niz
	public void Shrani(){
		String niz = "";
		
		for(int i = 0; i < skriti.size();i++){
			for(int j= 0; j < skriti.get(i).w.size(); j++)
				if(j != skriti.get(i).w.size())
					niz += skriti.get(i).w.get(j) + ",";
				else
					niz += skriti.get(i).w.get(j);
			if(i != skriti.size() - 1)
				niz+=";";
		}
		niz += ":";
		for(int i = 0; i < izhod.size();i++){
			for(int j= 0; j < izhod.get(i).w.size(); j++)
				if(j != izhod.get(i).w.size())
					niz += izhod.get(i).w.get(j) + ",";
				else
					niz += izhod.get(i).w.get(j);
			if(i != izhod.size() - 1)
				niz+=";";
		}
		writeToFile("NMreza", niz);
	}
	
	//Branje uteži v mreži iz niza
	public void Beri(){
		String niz = Odpri("NMreza");
		int a = 0, b = 0, c = 0;
		String[] nivoji = niz.split(":");
		String[] skrit = nivoji[1].split(",");
		String[] izhodn = nivoji[2].split(",");
		stevilo = izhodn.length;
		vhod = new Nivo(Integer.parseInt(nivoji[0]));
		skriti = new Nivo(skrit.length, vhod, skrit);
		izhod = new Nivo(izhodn.length, skriti, izhodn);
		
	}
	
	//Inicilizacije Nivojev
	public void InicilizirajMrezo(){
		rnd = new Random();
		vhod = new Nivo(StVhodnihN);
		skriti = new Nivo(StSkritihN, vhod, rnd);
		izhod = new Nivo(stevilo, skriti, rnd);
	}
	
	
	//Metoda za pisanje v datoteko
	private void writeToFile(String FileName, String txt){
        String sd = android.os.Environment.getExternalStorageDirectory().toString()+"/OCRPrevajalec/";
        (new File(sd)).mkdirs();
        try{
            File file = new File(sd,FileName+".txt");
            if(!file.exists()){
                file.createNewFile();
            }
            FileWriter f = new FileWriter(file);
            f.write(txt);
            f.flush();
            f.close();
        }
        catch(IOException e){
            Toast.makeText( getApplicationContext(), "OCR Prevajalec:\nNapaka pri ustvarjanju datoteke", Toast.LENGTH_SHORT).show();
        }
	}
	
	private String Odpri(String FileName)
	 {
	  String niz="";
	        String sd = android.os.Environment.getExternalStorageDirectory().toString()+"/OCRPrevajalec/";
	        (new File(sd)).mkdirs();
	        try{
	            File file = new File(sd,FileName+".txt");
	            if(!file.exists()){
	                return "";
	            }
	            InputStream instream = openFileInput(sd+FileName+".txt");
	        
	            // prepare the file for reading
	            InputStreamReader inputreader = new InputStreamReader(instream);
	            BufferedReader buffreader = new BufferedReader(inputreader);
	       
	            String line;
	       
	            // read every line of the file into the line-variable, on line at the time
	            while (( line = buffreader.readLine()) != null) {
	              //Prebral vrstico
	            }
	            niz+=line;
	          
	        }
	        catch(IOException e){
	            Toast.makeText( getApplicationContext(), "OCR Prevajalec:\nNapaka pri Branju datoteke", Toast.LENGTH_SHORT).show();
	            return "";
	        }
	        return niz;
	 }
	
	//Iteracijski del učenja
	private void Treniraj(List<posameznZnak> seznamtock){
		double napaka;
		int temp, st;
		int[] polje = new int[stevilo];
		for(int i=0; i< stevilo; i++){
			polje[i] = i;
		}
		do{
			napaka = 0;
			for(int i=0; i<stevilo; i++){
				st = rnd.nextInt(stevilo - 1);
				temp = polje[i];
				polje[i] = polje[st];
				polje[st] = temp;
			}
			for(int i=0; i<stevilo; i++){
				DodajVhode(seznamtock.get(polje[i]).vektorjiz);
				AktivirajNivo(skriti, vhod);
				AktivirajNivo(izhod, skriti);
				NapakeIzhodnegaNivoja(izhod, polje[i]);
				NapakeSkritegaNivoja(skriti, izhod);
				PopraviUtezi(izhod, 0.75d);
				PopraviUtezi(skriti, 1.25d);
				
				napaka += NapakaRezultata(i);
			}
		}while(napaka/stevilo > DovoljenaNapaka);
	}
	
	//Vstavljanje narisanih vrednosti
	private void DodajVhode(List<PointF> seznamtock){
		int j = 0;
		for(int i=0; i < seznamtock.size(); i++){
			vhod.get(j).y = (double)seznamtock.get(i).x;
			j++;
			vhod.get(j).y = (double)seznamtock.get(i).y;
			j++;
		}
	}
	
	//Aktivacija nivoja oz nevronov v enem nivoju
	private void AktivirajNivo(Nivo nivo, Nivo prejsnji){
		for(int i = 0; i < nivo.size(); i++){
			nivo.get(i).AktivirajNevron(prejsnji);
		}
	}
	
	//Računanje napake izhodnega nivoja
	private void NapakeIzhodnegaNivoja(Nivo izhod, int t){
		for(int i=0; i< izhod.size(); i++){
			izhod.get(i).DeltaIzhod(t == i);
		}
	}
	
	//Računanje napake skritega nivoja
	private void NapakeSkritegaNivoja(Nivo skriti, Nivo zgornji){
		for(int i = 0; i < skriti.size(); i++){
			skriti.get(i).DeltaSkriti(zgornji, i);
		}
	}
	
	//Popravljanje utezi
	private void PopraviUtezi(Nivo nivo, double mod){
		for(int i =0; i < nivo.size(); i++){
			nivo.get(i).PopraviUtezi(HitrostUcenja * mod);
		}
	}
	
	//Pridobitev napake iz nevrona
	private double NapakaRezultata(int znak){
		return 1 - izhod.get(znak).y;
	}
	
	//Iskanje razpoznanga znaka
	private int Najdi(){
		double max = Double.MIN_VALUE;
		int ind = 0;
		for(int i=0; i< izhod.size(); i++){
			if(izhod.get(i).y > max){
				max = izhod.get(i).y;
				ind = i;
			}
		}
		return ind;
	}

}

//Implemetacija enega nivoja nevronske mreže
final class Nivo implements List<Nevron>{
	
	private ArrayList<Nevron> seznam;  //Seznam nevronov v mreži
		
	//Ustvarjanje vhodnega nivoja
	public Nivo(int velikost){
		seznam = new ArrayList<Nevron>();
		for(int i=0; i<velikost;i++){
			this.add(new Nevron());
		}
	}
	
	//Ustvarjanje ostalih nivojev
	public Nivo(int velikost, Nivo nivo, Random rnd){
		seznam = new ArrayList<Nevron>();
		for(int i=0; i<velikost; i++){
			this.add(new Nevron(nivo, rnd));
		}
	}
	
	//Ustvarjanje ostalih nivojev
	public Nivo(int velikost, Nivo nivo, String[] utezi){
		seznam = new ArrayList<Nevron>();
		for(int i=0; i<velikost; i++){
			this.add(new Nevron(nivo, utezi[i].split(";")));
		}
	}
	
	public boolean add(Nevron nevron) {
		seznam.add(nevron);
		return true;
	}

	public void add(int arg0, Nevron arg1) {
	}

	public boolean addAll(Collection<? extends Nevron> arg0) {
		return false;
	}

	public boolean addAll(int arg0, Collection<? extends Nevron> arg1) {
		return false;
	}

	public void clear() {
		seznam.clear();
	}

	public boolean contains(Object arg0) {
		return false;
	}

	public boolean containsAll(Collection<?> arg0) {
		return false;
	}

	public Nevron get(int index) {
		return seznam.get(index);
	}

	public int indexOf(Object arg0) {
		return 0;
	}

	public boolean isEmpty() {
		return false;
	}

	public Iterator<Nevron> iterator() {
		return null;
	}

	public int lastIndexOf(Object arg0) {
		return 0;
	}

	public ListIterator<Nevron> listIterator() {
		return null;
	}

	public ListIterator<Nevron> listIterator(int arg0) {
		return null;
	}

	public Nevron remove(int arg0) {
		return null;
	}

	public boolean remove(Object arg0) {
		return false;
	}

	public boolean removeAll(Collection<?> arg0) {
		return false;
	}

	public boolean retainAll(Collection<?> arg0) {
		return false;
	}

	public Nevron set(int index, Nevron nevron) {
		return seznam.set(index, nevron);
	}

	public int size() {
		return seznam.size();
	}

	public List<Nevron> subList(int arg0, int arg1) {
		return null;
	}

	public Object[] toArray() {
		return null;
	}

	public <T> T[] toArray(T[] arg0) {
		return null;
	}
}

//Razred nevrona
final class Nevron{
	public ArrayList<Double> v; //Vhod iz prejšnega nivoja
	public ArrayList<Double> w; //Utež iz prejšnjega nivoja
	public double a; 	//Aktivacijska vrednost
	public double y; 	//Izhod iz nevrona
	public double ɸ; 	//
	
	//Prazen nevron
	public Nevron() {
		w = new ArrayList<Double>();
		v = null;
	}
	
	public Nevron(Nivo nivo, Random rnd){
		w = new ArrayList<Double>();
		v = new ArrayList<Double>();
		
		for(int i=0; i<nivo.size(); i++){
			w.add(rnd.nextDouble() * 2 -1);
			v.add(nivo.get(i).y);
		}
	}
	
	public Nevron(Nivo nivo, String[] utezi){
		w = new ArrayList<Double>();
		v = new ArrayList<Double>();
		
		for(int i=0; i<nivo.size(); i++){
			w.add(Double.parseDouble(utezi[i]));
			v.add(nivo.get(i).y);
		}
	}
	
	//Aktivacija nevrona
	public void AktivirajNevron(Nivo prejsnji){
		a = 0;
		for(int i=0; i< v.size(); i++){
			v.set(i, prejsnji.get(i).y);
			a += prejsnji.get(i).y * w.get(i);
		}
		y=1 / (1 + Math.exp(-a));
	}
	
	//Popravilo uteži nevrona
	public void PopraviUtezi(double α){
		for(int i=0; i < w.size(); i++){
			w.set(i, w.get(i) + α * ɸ * v.get(i));
		}
	}
	
	//Ugotavljanje napake izhodnega nevrona
	public void DeltaIzhod(boolean t){
		ɸ = y * (1d - y) * (t ? 1d : 0d - y);
	}
	
	//Ugotavljanje napake skritega nevrona
	public void DeltaSkriti(Nivo zgornji, int index){
		double delta = 0;
		for(int i=0; i< zgornji.size(); i++){
			delta += zgornji.get(i).ɸ * zgornji.get(i).w.get(index);
		}
		ɸ = y * ( 1 - y ) * delta;
	}
}
